package com.gitee.wsl.ext.array

import com.gitee.wsl.lang.ByteOrder

/**
 * Light shift of whole byte array by shiftBitCount bits.
 * This method will alter the input byte array.
 *
 *
 *
 * **Analysis**
 *
 *  * Time Complexity: `O(n)`
 *  * Space Complexity: `O(1)`
 *  * Alters Parameters: `true`
 *
 *
 *
 * @param byteArray     to shift
 * @param shiftBitCount how many bits to shift
 * @param byteOrder     endianness of given byte array
 * @return shifted byte array
 */
fun ByteArray.shiftLeft( shiftBitCount: Int, byteOrder: ByteOrder){
    val shiftMod = shiftBitCount % 8
    val carryMask = ((1 shl shiftMod) - 1).toByte()
    val offsetBytes = (shiftBitCount / 8)

    var sourceIndex: Int
    if (byteOrder == ByteOrder.BigEndian) {
        for (i in indices) {
            sourceIndex = i + offsetBytes
            if (sourceIndex >= this.size) {
                this[i] = 0
            } else {
                val src = this[sourceIndex]
                var dst = (src.toInt() shl shiftMod).toByte()
                if (sourceIndex + 1 < this.size) {
                    dst =
                        (dst.toInt() or (this[sourceIndex + 1].toInt() ushr (8 - shiftMod) and carryMask.toInt() and 0xff)).toByte()
                }
                this[i] = dst
            }
        }
    } else {
        for (i in this.indices.reversed()) {
            sourceIndex = i - offsetBytes
            if (sourceIndex < 0) {
                this[i] = 0
            } else {
                val src = this[sourceIndex]
                var dst = (src.toInt() shl shiftMod).toByte()
                if (sourceIndex - 1 >= 0) {
                    dst =
                        (dst.toInt() or (this[sourceIndex - 1].toInt() ushr (8 - shiftMod) and carryMask.toInt() and 0xff)).toByte()
                }
                this[i] = dst
            }
        }
    }
    //return byteArray
}

/**
 * Unsigned/logical right shift of whole byte array by shiftBitCount bits.
 * This method will alter the input byte array.
 *
 *
 *
 * **Analysis**
 *
 *  * Time Complexity: `O(n)`
 *  * Space Complexity: `O(1)`
 *  * Alters Parameters: `true`
 *
 *
 *
 * @param byteArray     to shift
 * @param shiftBitCount how many bits to shift
 * @param byteOrder     endianness of given byte array
 * @return shifted byte array
 */
fun ByteArray.shiftRight(shiftBitCount: Int, byteOrder: ByteOrder) {
    val shiftMod = shiftBitCount % 8
    val carryMask = (0xFF shl (8 - shiftMod)).toByte()
    val offsetBytes = (shiftBitCount / 8)

    var sourceIndex: Int
    if (byteOrder == ByteOrder.BigEndian) {
        for (i in indices.reversed()) {
            sourceIndex = i - offsetBytes
            if (sourceIndex < 0) {
                this[i] = 0
            } else {
                val src = this[sourceIndex]
                var dst = ((0xff and src.toInt()) ushr shiftMod).toByte()
                if (sourceIndex - 1 >= 0) {
                    dst =
                        (dst.toInt() or (this[sourceIndex - 1].toInt() shl (8 - shiftMod) and carryMask.toInt() and 0xff)).toByte()
                }
                this[i] = dst
            }
        }
    } else {
        for (i in this.indices) {
            sourceIndex = i + offsetBytes
            if (sourceIndex >= this.size) {
                this[i] = 0
            } else {
                val src = this[sourceIndex]
                var dst = ((0xff and src.toInt()) ushr shiftMod).toByte()
                if (sourceIndex + 1 < this.size) {
                    dst =
                        (dst.toInt() or (this[sourceIndex + 1].toInt() shl (8 - shiftMod) and carryMask.toInt() and 0xff)).toByte()
                }
                this[i] = dst
            }
        }
    }
    //return byteArray
}